from __future__ import annotations

from datetime import datetime, timezone

import time_machine
from django.test.testcases import TestCase

from tests.models import CustomManagerStatusModel, Status, StatusPlainTuple


class StatusModelTests(TestCase):
    model: type[Status] | type[StatusPlainTuple]

    def setUp(self) -> None:
        self.model = Status
        self.on_hold = Status.STATUS.on_hold
        self.active = Status.STATUS.active

    def test_created(self) -> None:
        with time_machine.travel(datetime(2016, 1, 1)):
            c1 = self.model.objects.create()
        self.assertTrue(c1.status_changed, datetime(2016, 1, 1))

        self.model.objects.create()
        self.assertEqual(self.model.active.count(), 2)
        self.assertEqual(self.model.deleted.count(), 0)

    def test_modification(self) -> None:
        t1 = self.model.objects.create()
        date_created = t1.status_changed
        t1.status = self.on_hold
        t1.save()
        self.assertEqual(self.model.active.count(), 0)
        self.assertEqual(self.model.on_hold.count(), 1)
        self.assertTrue(t1.status_changed > date_created)
        date_changed = t1.status_changed
        t1.save()
        self.assertEqual(t1.status_changed, date_changed)
        date_active_again = t1.status_changed
        t1.status = self.active
        t1.save()
        self.assertTrue(t1.status_changed > date_active_again)

    def test_save_with_update_fields_overrides_status_changed_provided(self) -> None:
        '''
        Tests if the save method updated status_changed field
        accordingly when update_fields is used as an argument
        and status_changed is provided
        '''
        with time_machine.travel(datetime(2020, 1, 1, tzinfo=timezone.utc)):
            t1 = Status.objects.create()

        with time_machine.travel(datetime(2020, 1, 2, tzinfo=timezone.utc)):
            t1.status = Status.on_hold
            t1.save(update_fields=['status', 'status_changed'])

        self.assertEqual(t1.status_changed, datetime(2020, 1, 2, tzinfo=timezone.utc))

    def test_save_with_update_fields_overrides_status_changed_not_provided(self) -> None:
        '''
        Tests if the save method updated status_changed field
        accordingly when update_fields is used as an argument
        with status and status_changed is not provided
        '''
        with time_machine.travel(datetime(2020, 1, 1, tzinfo=timezone.utc)):
            t1 = Status.objects.create()

        with time_machine.travel(datetime(2020, 1, 2, tzinfo=timezone.utc)):
            t1.status = Status.on_hold
            t1.save(update_fields=['status'])

        self.assertEqual(t1.status_changed, datetime(2020, 1, 2, tzinfo=timezone.utc))


class StatusModelPlainTupleTests(StatusModelTests):
    def setUp(self) -> None:
        self.model = StatusPlainTuple
        self.on_hold = StatusPlainTuple.STATUS[2][0]
        self.active = StatusPlainTuple.STATUS[0][0]


class StatusModelDefaultManagerTests(TestCase):

    def test_default_manager_is_not_status_model_generated_ones(self) -> None:
        # Regression test for GH-251
        # The logic behind order for managers seems to have changed in Django 1.10
        # and affects default manager.
        # This code was previously failing because the first custom manager (which filters
        # with first Choice value, here 'first_choice') generated by StatusModel was
        # considered as default manager...
        # This situation only happens when we define a model inheriting from an "abstract"
        # class which defines an "objects" manager.

        CustomManagerStatusModel.objects.create(status='first_choice')
        CustomManagerStatusModel.objects.create(status='second_choice')
        CustomManagerStatusModel.objects.create(status='second_choice')

        # ...which made this count() equal to 1 (only 1 element with status='first_choice')...
        self.assertEqual(CustomManagerStatusModel._default_manager.count(), 3)

        # ...and this one equal to 0, because of 2 successive filters of 'first_choice'
        # (default manager) and 'second_choice' (explicit filter below).
        self.assertEqual(CustomManagerStatusModel._default_manager.filter(status='second_choice').count(), 2)
